package com.cloudfun.campusshare.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.cloudfun.campusshare.common.constant.Code;
import com.cloudfun.campusshare.common.constant.MsgStatus;
import com.cloudfun.campusshare.common.constant.MsgType;
import com.cloudfun.campusshare.common.constant.SysDictType;
import com.cloudfun.campusshare.common.model.vo.MessageVo;
import com.cloudfun.campusshare.common.response.BaseResult;
import com.cloudfun.campusshare.common.response.BaseResultFactory;
import com.cloudfun.campusshare.common.response.PageResult;
import com.cloudfun.campusshare.common.security.AuthenticationHolder;
import com.cloudfun.campusshare.entity.MessageEntity;
import com.cloudfun.campusshare.entity.SystemDictionaryEntity;
import com.cloudfun.campusshare.entity.UserEntity;
import com.cloudfun.campusshare.repo.MessageEntityRepo;
import com.cloudfun.campusshare.repo.UserEntityRepo;
import com.cloudfun.campusshare.service.MessageService;
import com.cloudfun.campusshare.service.SysDictService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by Huqin on 2019/12/28
 */
@Slf4j
@Service
public class MessageServiceImpl implements MessageService {

    @Autowired
    private SysDictService sysDictService;

    @Autowired
    private MessageEntityRepo messageEntityRepo;

    @Autowired
    private UserEntityRepo userEntityRepo;

    @Override
    public void create(String bizId, String from, String to, MsgType msgType, List<String> subjectParams, List<String> contentParams) {
        // 融资贷款审核、放款确认暂时不发消息
        if (MsgType.LOAN_AUDIT_RESULT.equals(msgType)||MsgType.ISSUE_LOAN_AUDIT_RESULT.equals(msgType)) {
            return;
        }
        SystemDictionaryEntity msgSubjectMetaData = sysDictService.getByTypeAndKey(SysDictType.MSG_SUBJECT_TEMPLATE, msgType.toString());
        SystemDictionaryEntity msgContentMetaData = sysDictService.getByTypeAndKey(SysDictType.MSG_CONTENT_TEMPLATE, msgType.toString());
        String content = msgContentMetaData.getValue();
        for (int i = 0; i < contentParams.size(); i++) {
            String placeHolder = "#{" + i + "}";
            content = content.replace(placeHolder, Objects.isNull(contentParams.get(i))?"":contentParams.get(i));
        }
        String subject = msgSubjectMetaData.getValue();
        for (int i = 0; i < subjectParams.size(); i++) {
            String placeHolder = "#{" + i + "}";
            subject = subject.replace(placeHolder, subjectParams.get(i));
        }
        MessageEntity messageEntity = MessageEntity.builder()
                .type(msgType)
                .bizId(bizId)
                .sender(from)
                .recipient(to)
                .subject(subject)
                .content(content)
                .status(MsgStatus.UNREAD)
                .enabled(true)
                .build();
        messageEntityRepo.save(messageEntity);
    }

    @Override
    public PageResult<MessageVo> search(String value, MsgStatus status, MsgType msgType, Pageable pageable) {
        pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(Sort.Direction.DESC, "createTime"));
        Specification<MessageEntity> specification = (root, query, cb) -> {
            List<Predicate> predicateList = new ArrayList<>();
            predicateList.add(cb.equal(root.get("recipient"), AuthenticationHolder.getUserId()));
            if (null != status) {
                predicateList.add(cb.equal(root.get("status"), status));
            }
            if (null != msgType) {
                predicateList.add(cb.equal(root.get("type"), msgType));
            }
            // 只返回有效的
            predicateList.add(cb.equal(root.get("enabled"), true));

            if (StringUtils.isNotBlank(value)) {
                predicateList.add(cb.like(root.get("content"), "%" + value + "%"));
            }
            Predicate[] predicates = new Predicate[predicateList.size()];
            predicateList.toArray(predicates);
            return cb.and(predicates);
        };
        Page<MessageEntity> entityPage = messageEntityRepo.findAll(specification, pageable);
        long totalElements = entityPage.getTotalElements();
        Set<String> userIds = new HashSet<>();
        List<MessageVo> content = entityPage.getContent().stream()
                .map(messageEntity -> {
                    userIds.add(messageEntity.getSender());
                    MessageVo vo = new MessageVo();
                    BeanUtil.copyProperties(messageEntity, vo);
                    return vo;
                }).collect(Collectors.toList());

        Iterable<UserEntity> userEntities = userEntityRepo.findAllById(userIds);
        Map<String, String> idUsernameMap = new HashMap<>();
        for (UserEntity userEntity : userEntities) {
            idUsernameMap.put(userEntity.getId(), userEntity.getUsername());
        }
        content.forEach(messageVo -> {
            messageVo.setSenderName(idUsernameMap.get(messageVo.getSender()));
        });
        PageResult<MessageVo> result = new PageResult<>();
        result.setElements(content);
        result.setTotalSize(totalElements);
        return result;
    }

    @Override
    public MessageVo messageDetail(String id) {
        MessageEntity messageEntity = messageEntityRepo.findByRecipientAndIdAndEnabled(AuthenticationHolder.getUserId(), id, true);
        MessageVo vo = new MessageVo();
        BeanUtil.copyProperties(messageEntity, vo);
        userEntityRepo.findById(messageEntity.getSender()).ifPresent(sender -> {
            vo.setSenderName(sender.getUsername());
        });
        return vo;
    }

    @Override
    public void markAsRead(List<String> ids) {
        List<MessageEntity> messageEntities = messageEntityRepo.findByRecipientAndIdInAndEnabled(AuthenticationHolder.getUserId(), ids, true);
        messageEntities.forEach(messageEntity -> messageEntity.setStatus(MsgStatus.READ));
        messageEntityRepo.saveAll(messageEntities);
    }

    @Override
    public BaseResult<?> updateEnabled(List<String> ids, Boolean enabled) {
        List<MessageEntity> messageEntities = messageEntityRepo.findByRecipientAndIdInAndEnabled(AuthenticationHolder.getUserId(), ids, true);
        // 未读消息不能删除
        Optional<MessageEntity> unreadMsgOpt = messageEntities.stream().filter(messageEntity -> MsgStatus.UNREAD.equals(messageEntity.getStatus())).findAny();
        if (unreadMsgOpt.isPresent()) {
            return BaseResultFactory.produceEmptyResult(Code.MSG_UNREAD);
        }
        messageEntities.forEach(messageEntity -> messageEntity.setEnabled(enabled));
        messageEntityRepo.saveAll(messageEntities);
        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }
}
